/***************************************************************************
 *   Copyright (C) 2007 by Eduardo Robles Elvira   *
 *   edulix@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <errno.h>
#include <QTimer>
#include <QDebug>
#include <QMetaType>
#include <QDomDocument>
#include <iostream>
#include <QModelIndex>
#include <QMutex>
#include <QItemSelectionModel>
#include <QVariant>
#include "eventsmodel.h"
#include "konntrackitem.h"

using namespace std;

/**
 * Constructor of the Model. Initializes a timer that calls for an update of
 * the list of conntracks, and initializes the conntrack library enviroment.
 */
EventsModel::EventsModel(QObject *parent)
	: QAbstractTableModel(parent), et(this)
{
	qRegisterMetaType<QModelIndex>("QModelIndex");
	et.start();
}

/**
 * Destructor of the model
 */
 EventsModel::~EventsModel()
{
	et.terminate();
	et.wait();
}

/**
 * Extracts the information from a conntrack in XML and shows it in conntracks' list
 */
void EventsModel::listConntrack(char string[], nf_conntrack *ct)
{
	beginInsertRows(QModelIndex(), 0, 0);
	KonntrackItem *item = new KonntrackItem;
	items.prepend(new KonntrackItem);
	connect(item, SIGNAL(ipNameResolved()), this, SLOT(ipNameResolved()));
	
	items[0]->setConntrack(ct);
	
	QString buffer(string);
	QDomDocument domTree;
	if ( !domTree.setContent( buffer ) ) {
		qDebug() << "domTree error" << endl;
	}
	QDomElement root = domTree.documentElement();
	items[0]->setData((KonntrackCol)19, root.attribute("type"));
	QDomNode node = root.firstChild();
	while ( !node.isNull() ) {
		if ( node.isElement() && node.nodeName() == "meta" ) {
			QDomElement meta = node.toElement();
			processMeta( meta );
		}
		node = node.nextSibling();
	}
	
	endInsertRows();
}


void EventsModel::setColText(int col, QDomNode &node)
{
	QDomText textChild = node.firstChild().toText();
	if ( !textChild.isNull() ) {
		items[0]->setData((KonntrackCol)col, textChild.nodeValue());
	}
}

void EventsModel::processMeta(QDomElement &meta)
{
	int type=0; // type 0 means direction, 1 means independent
	int col=0;
	if(meta.attribute("direction") == "original") {
		type = 0;
	} else if(meta.attribute("direction") == "reply") {
		type = 0;
		col = 7; // col starts in reply columns
	} else if(meta.attribute("direction") == "independent") {
		type = 1;
	} else {
		qDebug() << "unknown attribute: " << meta.attribute("direction") << endl;
		return;
	}
	if(type == 0) {
		QDomNode node = meta.firstChild();
		QString proto[2] = {"", ""};
		while ( !node.isNull() ) {
			if ( node.isElement() ) {
				if ( node.nodeName() == "layer3" ) {
					proto[0] = node.toElement().attribute( "protoname" );
					QDomNode node2 = node.firstChild();
					while ( !node2.isNull() ) {
						if ( node2.isElement() ) {
							if( node2.nodeName() == "src" )
								setColText(col + 1, node2);
							else if( node2.nodeName() == "dst" )
								setColText(col + 3, node2);
						}
						node2 = node2.nextSibling();
					}
				} else if ( node.nodeName() == "layer4" ) {
					proto[1] = node.toElement().attribute( "protoname" );
					QDomNode node2 = node.firstChild();
					while ( !node2.isNull() ) {
						if ( node2.isElement() ) {
							if( node2.nodeName() == "sport" )
								setColText(col + 2, node2);
							else if( node2.nodeName() == "dport" )
								setColText(col + 4, node2);
						}
						node2 = node2.nextSibling();
					}
				} else if ( node.nodeName() == "counters" ) {
					QDomNode node2 = node.firstChild();
					while ( !node2.isNull() ) {
						if ( node2.isElement() ) {
							if( node2.nodeName() == "packets" )
								setColText(col + 5, node2);
							else if( node2.nodeName() == "bytes" )
								setColText(col + 6, node2);
						}
						node2 = node2.nextSibling();
					}
				}
			}
			node = node.nextSibling();
		}
		items[0]->setData((KonntrackCol)col, proto[0] + "/" + proto[1]);
	} else if(type == 1) {
		QDomNode node = meta.firstChild();
		bool assured = false, unreplied = false;
		while ( !node.isNull() ) {
			if ( node.isElement() ) {
				if( node.nodeName() == "timeout" )
					setColText(14, node);
				else if( node.nodeName() == "mark" )
					setColText(15, node);
				else if( node.nodeName() == "use" )
					setColText(16, node);
				else if( node.nodeName() == "assured" ) {
					items[0]->setData((KonntrackCol)17, "true");
					assured = true;
				}
				else if( node.nodeName() == "unreplied" ) {
					items[0]->setData((KonntrackCol)18, "true");
					assured = true;
				}
			}
			node = node.nextSibling();
		}
		
		if(!assured)
			items[0]->setData((KonntrackCol)17, "false");
		
		if(!unreplied)
			items[0]->setData((KonntrackCol)18, "false");
	}
}

QVariant EventsModel::data(const QModelIndex &index, int role) const
{
	return data(index, role, true);
}

QVariant EventsModel::data(const QModelIndex &index, int role, bool translated) const
{
	if (!index.isValid())
		return QVariant();

	if (role != Qt::DisplayRole)
		return QVariant();

	return items[index.row()]->getData(index.column(), translated);
}

QVariant EventsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
		switch (section) {
			case ORIG_PROTO:
				return tr("Orig protocols");
			case ORIG_SRC_IP:
				return tr("Orig src");
			case ORIG_SPORT:
				return tr("Orig sport");
			case ORIG_DST_IP:
				return tr("Orig dst ip");
			case ORIG_DPORT:
				return tr("Orig dport");
			case ORIG_PACKETS:
				return tr("Orig packets");
			case ORIG_BYTES:
				return tr("Orig bytes");
			case REPLY_PROTO:
				return tr("Orig protocols");
			case REPLY_SRC_IP:
				return tr("Orig src ip");
			case REPLY_SPORT:
				return tr("Orig sport");
			case REPLY_DST_IP:
				return tr("Orig dst ip");
			case REPLY_DPORT:
				return tr("Orig dport");
			case REPLY_PACKETS:
				return tr("Orig packets");
			case REPLY_BYTES:
				return tr("Orig bytes");
			case TIMEOUT:
				return tr("Timeout");
			case MARK:
				return tr("Mark");
			case USE:
				return tr("Use");
			case ASSURED:
				return tr("Assured");
			case UNREPLIED:
				return tr("Unreplied");
			case FLOW_TYPE:
				return tr("Flow type");
			case NUMBER:
				return tr("No.");
			default:
				return QVariant();
		}
	}
	// never reach here
	return QVariant();
}

int EventsModel::rowCount(const QModelIndex &/*parent*/) const
{
	return items.size();
}

int EventsModel::columnCount(const QModelIndex &/*parent*/) const
{
	return 21;
}

int EventsModel::EventsThread::event_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
{
	char buf[1024];
	EventsModel *parent = (EventsModel *)data;

	nfct_snprintf(buf, 1024, ct, type, NFCT_O_XML, 0);
	parent->listConntrack(buf, ct);
	return NFCT_CB_STOLEN;
}

void EventsModel::EventsThread::run()
{
	nfct_catch(this->h);
}

EventsModel::EventsThread::EventsThread(EventsModel *parent)
	: QThread(), parent(parent)
{
	h = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
	if (!h) {
		qDebug() << "nfct_open" << endl;
		perror("nfct_open");
		::exit(-1);
	}
	parent->setHandle(h);
	
	nfct_callback_register(h, NFCT_T_ALL, EventsModel::EventsThread::event_cb, parent);
}

EventsModel::EventsThread::~EventsThread()
{
	nfct_close(h);
}

void  EventsModel::ipNameResolved()
{
	emit dataChanged(index(1,0), index(items.size()-1,20));
}



void  EventsModel::clearEvents()
{
	items.clear();
	emit reset();
}


